package org.scboy.common.dynamic.node;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.scboy.common.dynamic.DynamicScript;
import org.scboy.common.dynamic.edge.BaseEdge;
import org.scboy.common.dynamic.endpoint.BaseEndpoint;
import org.scboy.utils.entitys.coobi.Model;

import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

/**
 * 基础节点
 */
public abstract class BaseNode {
	
	protected DynamicScript script;
	
	/**
	 * 分支开始节点
	 */
	private BaseNode branchNode;
	
	/**
	 * 数据缓存
	 */
	private Model cacheData=Model.create();
	
	/**
	 * 节点的ID
	 */
	private String id;
	
	/**
	 * 节点名称
	 */
	private String nodeName;
	
	/**
	 * 流入节点
	 */
	private BaseEndpoint runForm;
	

	/**
	 * 流出节点
	 */
	private Map<String, BaseEndpoint>  runTo=new HashMap<String, BaseEndpoint>();
	
	/**
	 * 输入点
	 */
	private Map<String, BaseEndpoint> inputEndpoint=new HashMap<String, BaseEndpoint>();
	
	/**
	 * 输出点
	 */
	private Map<String, BaseEndpoint> outPutEndpoint=new HashMap<String, BaseEndpoint>();
	
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getNodeName() {
		return nodeName;
	}

	public void setNodeName(String nodeName) {
		this.nodeName = nodeName;
	}

	public BaseEndpoint getRunForm() {
		return runForm;
	}

	public void setRunForm(BaseEndpoint runForm) {
		this.runForm = runForm;
	}

	public BaseNode getRunTo(String dataKey) {
		BaseEndpoint _runTo = runTo.get(dataKey);
		if(_runTo==null) {
			if(branchNode==null) {
				throw new RuntimeException(getNodeName()+"执行断开，找不到下一个执行节点。");
			}else {
				branchNode.setData("_index", ((int)branchNode.getData("_index"))+1);
				return branchNode;
			}
		}else {
			//下个执行节点
			List<BaseEdge> _inputEdges = _runTo.getOutputEdge();
			if(_inputEdges.size()>1) {
				throw new RuntimeException(getNodeName()+":"+_runTo.getName()+"只能有一个流出连接,现在有"+_inputEdges.size()+"个连接。");
			}else if(_inputEdges.size()==0) {
				if(branchNode==null) {
					throw new RuntimeException(getNodeName()+"执行断开，找不到下一个执行节点。");
				}else {
					branchNode.setData("_index", ((int)branchNode.getData("_index"))+1);
					return branchNode;
				}
			}else {
				BaseNode sourceNode=_inputEdges.get(0).getTargetNode();
				if(sourceNode==null) {
					throw new RuntimeException(getNodeName()+"执行断开，找不到下一个执行节点。");
				}else {
					//如果分支跟节点不存在，加入分支根节点。
					if(branchNode!=null && sourceNode.getBranchNode()==null) {
						sourceNode.setBranchNode(this.getBranchNode());
					}
					return sourceNode;
				}
			}
		}
	}

	public void setRunTo(BaseEndpoint runTo) {
		this.runTo.put(runTo.getDataKey(), runTo);
	}
	
	public BaseEndpoint getInputEndpoint(String dataKey) {
		return this.inputEndpoint.get(dataKey);
	}


	public void setInputEndpoint(BaseEndpoint inputEndpoint) {
		String dataKey=inputEndpoint.getDataKey();
		this.inputEndpoint.put(dataKey, inputEndpoint);
	}


	public BaseEndpoint getOutPutEndpoint(String dataKey) {
		return outPutEndpoint.get(dataKey);
	}

	public void setOutPutEndpoint(BaseEndpoint outPutEndpoint) {
		this.outPutEndpoint.put(outPutEndpoint.getDataKey(), outPutEndpoint);
	}

	public Object getData(String dataKey) {
		return cacheData.get(dataKey);
	}
	
	public Object setData(String dataKey,Object data) {
		return cacheData.put(dataKey, data);
	}
	
	public Model getDataAll() {
		return cacheData;
	}
	
	public void setBranchNode(BaseNode branchNode) {
		this.branchNode = branchNode;
	}

	public BaseNode getBranchNode() {
		return branchNode;
	}
	
	/**
	 * 获取来源的值
	 * @param <T>
	 * @param dateKey 数据键
	 * @param must 是否必须
	 * @param clazz 泛型
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T getSourceData(String dateKey,boolean must,Class<T> clazz) {
		BaseEndpoint inputParamKeyEndpoint = this.getInputEndpoint(dateKey);
		List<BaseEdge> inputParamKeyEdges=inputParamKeyEndpoint.getInputEdge();
		if(inputParamKeyEdges.size() > 1) {
			throw new RuntimeException(getNodeName()+":"+inputParamKeyEndpoint.getName()+"只能有一个入参,现在有"+inputParamKeyEdges.size()+"个连接。");
		}else if(inputParamKeyEdges.size()==1) {
			return (T)inputParamKeyEdges.get(0).getSourceEndpoint().getData();
		}else {
			if(must) {
				throw new RuntimeException(getNodeName()+":"+inputParamKeyEndpoint.getName()+"没有输入，无法执行。");
			}
			return null;
		}
	}
	
	/**
	 * 获取来源的值
	 * @param <T>
	 * @param dateKey 数据键
	 * @param must 是否必须
	 * @param clazz 泛型
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> getSourceListData(String dateKey,boolean must,Class<T> clazz) {
		BaseEndpoint inputParamKeyEndpoint = this.getInputEndpoint(dateKey);
		List<BaseEdge> inputParamKeyEdges=inputParamKeyEndpoint.getInputEdge();
		if(inputParamKeyEdges.size() > 1) {
			throw new RuntimeException(getNodeName()+":"+inputParamKeyEndpoint.getName()+"只能有一个入参,现在有"+inputParamKeyEdges.size()+"个连接。");
		}else if(inputParamKeyEdges.size()==1) {
			return (List<T>)inputParamKeyEdges.get(0).getSourceEndpoint().getData();
		}else {
			if(must) {
				throw new RuntimeException(getNodeName()+":"+inputParamKeyEndpoint.getName()+"没有输入，无法执行。");
			}
			return new ArrayList<T>();
		}
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> getSourceDatas(String dateKey,boolean must,Class<T> clazz) {
		BaseEndpoint inputParamKeyEndpoint = this.getInputEndpoint(dateKey);
		List<BaseEdge> inputParamKeyEdges=inputParamKeyEndpoint.getInputEdge();
		if(inputParamKeyEdges.size() == 0 && must ) {
			throw new RuntimeException(getNodeName()+":"+inputParamKeyEndpoint.getName()+"必须传入参数才能执行，当前脚本未传入参数。");
		} else {
			List<T> datas=new ArrayList<T>();
			for (BaseEdge baseEdge : inputParamKeyEndpoint.getInputEdge()) {
				T data=(T)baseEdge.getSourceEndpoint().getData();
				datas.add(data);
			}
			return datas;
		}
	}
	/**
	 * 节点创建方法
	 * @param object
	 * @return
	 */
	public void create(JSONObject object,DynamicScript script) {
		this.script=script;
		this.create(object);
	}
	
	public abstract void create(JSONObject object);
	
	/**
	 * data转换字符串
	 * @param object
	 * @return
	 */
	public String entityToString(Object object) {
		JSONConfig config=new JSONConfig();
		config.setIgnoreNullValue(false);
		return JSONUtil.toJsonStr(object,config);
	}
	/**
	 * 执行节点
	 */
	public abstract Object run();
}
